#include <thread>
#include <iostream>
#include <mutex>
#include <condition_variable>
#include <list>

using std::cout;
using std::endl;

template<typename T>
class SimpleSyncQueue{
    public:
        SimpleSyncQueue(){}
        void Put(const T & x)
        {
            std::lock_guard<std::mutex> locker(m_mutex);
            m_queue.push_back(x);
            m_notEmpty.notify_one();
        }

        void Take(T &x)
        {
            std::unique_lock<std::mutex> locker(m_mutex);
            m_notEmpty.wait(locker, [this]{
                    if(m_queue.empty())
                    {
                        cout << "queue is empty" << endl;
                    }
                    return !m_queue.empty();
                    }); 
            /* 通常，该函数通过在另一个线程中调用成员 notify_one 或成员 notify_all 来通知唤醒。
             * 但是某些实现可能会在没有调用任何这些函数的情况下产生虚假的唤醒调用。因此，使用此功能的用户应确保满足其恢复条件。 
             * 如果指定了函数 ，则函数仅在 返回 false 时阻塞，并且通知只能在变为 true 时解除线程阻塞（这对于检查虚假唤醒调用特别有用）。 */
            x = m_queue.front();
            m_queue.pop_front();
        }

        bool Empty()
        {
            std::lock_guard<std::mutex> locker(m_mutex);
            return m_queue.empty();
        }

        size_t Size()
        {
            std::lock_guard<std::mutex> locker(m_mutex);
            return m_queue.size();
        }

    private:
        std::list<T> m_queue;
        std::mutex m_mutex;
        std::condition_variable m_notEmpty;
};

SimpleSyncQueue<int> queue;
void fun()
{
    int x = 0;
    for(int i = 0; i < 10; i++)
    {
        queue.Take(x); //取
        cout << " | Take :" << x << endl;
    }

}

void fun2()
{
    for(int i = 0; i < 20; i++)
    {
        queue.Put(i); //放
        cout << " | Put:" << i << endl;
        //延时一秒
        std::this_thread::sleep_for(std::chrono::milliseconds(1000));
    }
}

void fun3(SimpleSyncQueue<int>  queue)
{

}
int main(void)
{
     //我声明了一个模板类(里面实现了一个同步队列)，实例化后，能否， 作为参数 传递到子线程中？
     //

    SimpleSyncQueue<int> queue;
    std::thread f1(fun);
    std::thread f2(fun2);
    //std::thread f3(fun3, std::ref(queue));


    f1.join();
    f2.join();
    return 0;
}
